﻿using AudioStudio;
using com.ootii.Actors.AnimationControllers;
using com.ootii.Geometry;
using Pathfinding;
using System;
using System.Threading;
using UnityEngine;
using Yoozoo.Core.Common;
using Yoozoo.Gameplay.Liberty.Pool;
using Yoozoo.Mars.Got;

namespace Yoozoo.Gameplay.Liberty.AI
{
    public class LocomotionAgent : MonoBehaviour
    {
        // 寻路重力刷新间隔
        public static float GravityUpdateDuration = 1f;
        
        /// <summary>
        /// 开启CharacterController的范围
        /// </summary>
        public static float _OpenCharacterControllerDistance = 20;
        
        private Transform ExternalForceRigid;
        [SerializeField]
        private Transform syncTransform;

        public Transform SyncTransform
        {
            get
            {
                if (syncTransform == null)
                {
                    syncTransform = transform.parent;
                }
                return syncTransform;
            }
            set => syncTransform = value;
        }
        // /// <summary>
        // /// The current state of the controller including speed, direction, etc.
        // /// </summary>
        // public MotionState State = new MotionState();
        
        public AISeeker seeker;
        public AIData npcData;
        public AIEntity aiEntity;
        public CharacterController characterController;
        
        public bool immediateRotate = false;
        public int banRotationFrame = 1;
        
        // [Header("限制最大模拟输入")][Range(0,2)]
        // public float simInputMax = 2f;
        // [Header("动画速度参数与实际速度的缩放比")]
        // public float animSpeedScale = 1f;

        // [Range(0.01f,2f)]
        // public float maxAnimSpeedScale = 1.2f;
        // [Range(0.01f,0.5f)]
        // public float minAnimSpeedScale = 0.5f;
        // [Range(1,60)]
        // public float animRotSpeedFactor = 16f;
        
        public string animHorName = "WalkH";
        private int _animHorId;
        
        public string animVerName = "WalkV";
        private int _animVerId;

        public string animSpeedName = "Speed";
        private int _animSpeedId;
        private bool _setSpeedAnimParam;
        
        
        /// <summary>
        /// Tracks the root motion so we can apply it later
        /// </summary>
        private Vector3 mRootMotionMovement = Vector3.zero;

        /// <summary>
        /// Tracks the root motion rotation so we can apply it later
        /// </summary>
        private Quaternion mRootMotionRotation = Quaternion.identity;

        /// <summary>
        /// Current movement caused by the motion. This should NOT be
        /// multiplied by delta-time to create displacement
        /// </summary>
        protected Vector3 mMovement = Vector3.zero;
        public Vector3 Movement
        {
            get { return mMovement; }
        }
        /// <summary>
        /// Amount of rotation (yaw) caused by the motion. This should NOT be
        /// multiplied by delta-time to create angular displacement. This is
        /// also considered a "local" rotation. So, it is relative to the current
        /// rotation.
        /// </summary>
        protected Quaternion mRotation = Quaternion.identity;
        public Quaternion Rotation
        {
            get { return mRotation; }
        }

        /// <summary>
        /// We use these classes to help smooth the input values so that
        /// movement doesn't drop from 1 to 0 immediately.
        /// </summary>
        protected FloatValue mInputX = new FloatValue(0f, 10);
        protected FloatValue mInputY = new FloatValue(0f, 10);
        protected FloatValue mInputMagnitude = new FloatValue(0f, 15);

        /// <summary>
        /// Fields to help smooth out the mouse rotation
        /// </summary>
        protected float mYaw = 0f;
        protected float mYawTarget = 0f;
        protected float mYawVelocity = 0f;

        private Animator _Animator;

        public Action lateUpdate;
        // public bool forceUpdateRotation =false;
        private bool isMoving;
        private float soundTime;
        private Vector3 m_ExternalForce;
        public float m_ExternalForceDamping = 0.1f;
        public void RegistSeeker(AISeeker seeker,AIEntity entity,CharacterController characterController)
        {
            this.seeker = seeker;
            this.npcData = entity.data;
            aiEntity = entity;
            this.characterController = characterController;
        }

        public void SetForceUpdateTargetRotation(Quaternion targetRotation)
        {
            mYawQuater = targetRotation;
            aiEntity.SetRotation(targetRotation.eulerAngles);
            seeker.transform.rotation = targetRotation;
            seeker.transform.position = SyncTransform.position; // 站在原地，一段时间内只使用该方法旋转（例如射击），Seeker的位置会发生变化？？这里校正一下
        }
        public void SetForceUpdateTargetRotationAndPosition(Quaternion targetRotation,Vector3 targetPosition)
        {
            mYawQuater = targetRotation;
            aiEntity.SetPositionAndRotation(targetPosition,targetRotation.eulerAngles);
            seeker.transform.rotation = targetRotation;
            seeker.transform.position = targetPosition; // 站在原地，一段时间内只使用该方法旋转（例如射击），Seeker的位置会发生变化？？这里校正一下
        }
        public void GetAnimId()
        {
            _animHorId = Animator.StringToHash(animHorName);
            _animVerId = Animator.StringToHash(animVerName);

            if (!string.IsNullOrEmpty(animSpeedName))
            {
                _animSpeedId = Animator.StringToHash(animSpeedName);
                _setSpeedAnimParam = true;
            }
            else
            {
                _setSpeedAnimParam = false;
            }
        }

  
        
        private void Awake()
        {
            GetAnimId();
            _Animator = GetComponent<Animator>();
        }

        private void OnEnable()
        {
            aiEntity?.ResetFixedPosition();
            
            // 切换Animator的更新方式和裁剪模式
            _Animator.updateMode = AnimatorUpdateMode.Normal;
            _Animator.cullingMode = AnimatorCullingMode.CullUpdateTransforms; // 不能使用CullCompletely，会在不可见的时候无法触发AnimationEvent
        }

        public bool isVehicleBarge;
        public bool isVehicleScratch;
        public Vector3 bargeDir;
        
        public AnimationCurve leapCurve;
        public float vehicleBargeTime;
        public float vehicleBargeSpeed = 1;
        public float vehicleBargeHeight = 2;
        
        public float vehicleBargeDistance = 2f;

        public Transform carTransform;
        public Vector3 relativeCarPos;  // 开始进入车辆时，相对于车辆的局部坐标
        public Quaternion relativeCarRot; // 开始进入车辆时，相对于车辆的局部旋转


        public void SetCarEntity(Transform entity)
        {
            carTransform = entity;
            SetRelative();
        }

        public void SetRelative()
        {
            relativeCarPos = carTransform.InverseTransformPoint(SyncTransform.position);
            relativeCarRot = Quaternion.LookRotation(carTransform.InverseTransformDirection(SyncTransform.forward));
        }

        /// <summary>
        /// 刷新CharacterController的启用状态
        /// </summary>
        /// <param name="forceAcitve">强制开启</param>
        void RefreshCharacterControllerEnabled(bool forceAcitve = false)
        {
            if (LibertyAIUtils.PlayerController == null)
                return;

            if (!LibertyConst.IsOpenAIPathPerformanceMode)
            {
                // 不开启性能模式，直接开启CharacterController
                if (!characterController.enabled)
                {
                    characterController.enabled = true;
                }
                return;
            }

            bool active = forceAcitve;
            if (!forceAcitve)
            {
                AIEntity playerAIEntity = LibertyAIUtils.PlayerController.playerEntity.AIEntity;
                if (playerAIEntity && (playerAIEntity.data.npcStatus & NpcStatus.InCar)>0)
                {
                    // 玩家开车时，屏蔽npc的CharacterContrroller，防止车辆被阻挡
                    active = false;
                }
                else if((int)QualityManager.Inst.CurrentQuality >= (int)QualityEnum.High 
                        && ((npcData.npcStatus & NpcStatus.Battle) > 0 || (npcData.npcStatus & NpcStatus.Escape) > 0))
                {
                    // 高端机战斗状态强制开启
                    active = true;
                }
                else
                {
                    // 如果非强制开启，就根据距离检测是否开启
                    active = npcData.distanceToPlayer <= _OpenCharacterControllerDistance;
                }
            }
            
            bool needUpdate = characterController.enabled != active;
            if (needUpdate)
            {
                characterController.enabled = active;
                
                // 切换seeker组件的重力策略，近处使用原生逻辑，远处使用紧贴地面1s做一次射线检测
                if (seeker)
                {
                    if (active)
                    {
                        seeker.aiPath.gravityStrategy = AIBase.GravityStrategy.PathfindingNative;
                    }
                    else
                    {
                        seeker.aiPath.gravityStrategy = AIBase.GravityStrategy.CloseToGround;
                        if (LibertyConst.OverrideAIPathGravityUpdateDuration)
                        {
                            seeker.aiPath.gravityUpdateDuration = LibertyConst.AIPathGravityUpdateDuration;
                        }
                    }
                }
                
                // 切换Animator的更新方式和裁剪模式
                _Animator.updateMode = AnimatorUpdateMode.Normal;
                _Animator.cullingMode = AnimatorCullingMode.CullUpdateTransforms; // 不能使用CullCompletely，会在不可见的时候无法触发AnimationEvent
            }
        }

        private int _lastFixedUpdateFrameCount = -1;
        void FixedUpdate()
        {
            // 这里FixedUpdate一帧只需要调用一次，防止多次调用。
            if (_lastFixedUpdateFrameCount == Time.frameCount)
            {
                return;
            }
            _lastFixedUpdateFrameCount = Time.frameCount;

            if (_Animator == null)
            {
                return;
            }
            
            if (IsCarState())
            {
                DoCarLocomotion();
                return;
            }
            if (npcData.npcStatus == NpcStatus.VehicleBarge)
            {
                DoVehicleBargeLocomotion();
                return;
            }
            
            if((npcData.npcStatus & NpcStatus.PlayerBarge) > 0 || npcData.animationStateHasRootMotion)
            {
                DoAnimatorLocomotion();
                return;
            }

            if (m_ExternalForce!=Vector3.zero)
            {
                SetExternalForcePos();
            }
            else
            {
                // Npc或玩家自动寻路时
                if (IsNavigation())
                {
                    SetNavigationLocomotion();
                    return;
                }
                SetSeekerPos();
                // 根据距离开启CharacterController
                RefreshCharacterControllerEnabled();
            }
        }

        private bool _startAddForce = false;
        public float addForceTime = 1f;
        private float timer = 0f;
        private Rigidbody rigidbody;
        private Vector3 _explosionPosition;
        public void AddForce(
            Vector3 force)
        {
            m_ExternalForce = force;
            if (ExternalForceRigid == null)
            {
                ExternalForceRigid = LibertyPoolMgr.GetInstance("LibertyPoolMgr").GetEffectObject(90001);
            }
            ExternalForceRigid.gameObject.SetActive(true);
            ExternalForceRigid.position = aiEntity.data.CurrentLerpPositin;
            var rigidbody = ExternalForceRigid.gameObject.GetComponent<Rigidbody>();
            rigidbody.velocity = Vector3.zero;
            _explosionPosition = force;
            _startAddForce = true;
            timer = 0;
            Timers.inst.Add(0.02f,1, (obj) =>
            {
                rigidbody.AddForce(force);
                characterController.enabled = false;
                seeker.Stop();
                aiEntity.data.isBombFly = true;
            });
        }
        private void SetExternalForcePos()
        {
            if (_startAddForce) 
            {
                timer += Time.fixedDeltaTime;
                if (ExternalForceRigid!=null)
                {
                    aiEntity.SetPositionAndRotation(ExternalForceRigid.position);
                    SetSeekerPos();
                }
                if (timer >= addForceTime) 
                {
                    var rigidbody = ExternalForceRigid.GetComponent<Rigidbody>();
                    rigidbody.velocity = Vector3.zero; // 重置速度
                    rigidbody.angularVelocity = Vector3.zero; // 重置角速度
                    _startAddForce = false;
                    m_ExternalForce = Vector3.zero;
                    aiEntity.data.isBombFly = false;
                    LibertyPoolMgr.GetInstance("LibertyPoolMgr").RecycleEffect(90001,ExternalForceRigid);
                    ExternalForceRigid = null;
                }
            }
            
        }

        private bool IsCarState()
        {
            return (npcData.npcStatus & NpcStatus.EnterCar) > 0 ||
                   (npcData.npcStatus & NpcStatus.InCar) > 0 ||
                   (npcData.npcStatus & NpcStatus.ExitCar) > 0 ||
                   (npcData.npcStatus & NpcStatus.ExpelCar) > 0;
        }
        /// <summary>
        /// Npc或玩家自动寻路时
        /// </summary>
        /// <returns></returns>
        private bool IsNavigation()
        {
            return (npcData.baseAnimationState == AnimationState.Locomotion) ||
                   (npcData.aimingState != AimingState.None) ||
                   (npcData.uniqueId == LibertyAIUtils.PLAYER_AI_ID);
        }
        private void DoCarLocomotion()
        {
            if (carTransform == null)
            {
                return;   
            }            
            // 这里使用车辆的局部坐标系对角色进行移动，防止车辆移动导致位置不正确
            // SyncTransform.localPosition += Quaternion.Inverse(SyncTransform.rotation) * _Animator.deltaPosition;
            // SyncTransform.rotation *= _Animator.deltaRotation;
            //
            // UnityEngine.Debug.LogError(SyncTransform.localPosition);
                
            Quaternion quaternion = relativeCarRot;
            quaternion *= mRootMotionRotation;
                
            var forward = (quaternion * Vector3.forward).normalized;
            forward = carTransform.TransformDirection(forward);
                
            if (mRootMotionRotation.eulerAngles.magnitude > 0.002)
            {
                relativeCarRot = quaternion;
                SyncTransform.forward = forward;
            }
                
            Vector3 pos = relativeCarPos;
            pos += carTransform.InverseTransformVector(SyncTransform.rotation * mRootMotionMovement);
            if (mRootMotionMovement.magnitude > 0.002)
            {
                relativeCarPos = pos;
                // AssignFixedLocation();
                // AssignFixedRotation();
                aiEntity.SetPositionAndRotation(carTransform.TransformPoint(pos), SyncTransform.eulerAngles);
            }
            // SetSeekerPos();
        }

        private void DoVehicleBargeLocomotion()
        {
            vehicleBargeTime += Time.deltaTime;

            Vector3 deltaPosition = Vector3.zero;
                
            if (isVehicleBarge)
            {
                deltaPosition = _Animator.deltaPosition;
                    
                deltaPosition = Vector3.Dot(deltaPosition, bargeDir) * bargeDir * 1.414f * vehicleBargeDistance;
                float lastH = transform.position.y;
                float h = leapCurve.Evaluate(vehicleBargeTime * vehicleBargeSpeed) * vehicleBargeHeight;
                    
                deltaPosition.y = h - lastH;
            }
            else if (isVehicleScratch)
            {
                deltaPosition = _Animator.deltaPosition;
                deltaPosition = Vector3.Dot(deltaPosition, bargeDir) * bargeDir;
            }
                
            // 动画驱动的位移
            // SyncTransform.rotation *= mRootMotionRotation;
            // SyncTransform.position += deltaPosition;
                
            AssignFixedLocation(SyncTransform.position + deltaPosition);
            AssignFixedRotation((SyncTransform.rotation * mRootMotionRotation).eulerAngles);
            // transform.localPosition = Vector3.zero;
            // transform.localEulerAngles = Vector3.zero;
            // SetSeekerPos();
        }
        
        private void DoAnimatorLocomotion()
        {
            // 动画驱动的位移
            // 强制开启CharacterController
            RefreshCharacterControllerEnabled(true);
                
            // SyncTransform.rotation *= mRootMotionRotation;
            var targetRotation = (SyncTransform.rotation * mRootMotionRotation).eulerAngles;
            AssignFixedRotation(targetRotation);
            Move(Quaternion.Euler(targetRotation) * mRootMotionMovement);

            // SetSeekerPos();
        }

        private void SetNavigationLocomotion()
        {
            // 寻路驱动的位移
            if (!npcData.isAlive)
            {
                return;
            }
                
            if (seeker == null)
            {
                return;
            }

            // 根据距离开启CharacterController
            RefreshCharacterControllerEnabled();
            // 模拟输入值
            float lDeltaTime = Time.deltaTime;
            seeker.OnLateUpdate(false, lDeltaTime); // 得到期望行走的位置
            // ProcessUserInput(); // 将期望值转为输入值
            // UpdateMotions(lDeltaTime);
            UpdateMovement(lDeltaTime); // 得到动画位移值
            UpdateRotation();
            AssignFixedLocation(seeker.transform.position);
            // 设置动画参数
            SetAnimatorProperties();
        }

        // private void Update()
        // {
        //     if (!IsNavigation() || characterController.enabled)
        //     {
        //         SetSeekerPos();
        //     }
        // }

        private void SetSeekerPos()
        {
            if (seeker != null)
            {
                seeker.transform.position = SyncTransform.position;
                seeker.transform.rotation = SyncTransform.rotation;
            }
        }
    
        private void AssignFixedLocation(Vector3 targetPosition)
        {
            aiEntity.AssignFixedLocation(targetPosition);
        }
        
        private void AssignFixedRotation(Vector3 targetRotation = default)
        {
            aiEntity.AssignFixedRotation(targetRotation);
        }

        
        private void SetAnimatorProperties()
        {
            Vector3 aimingForward = aiEntity.data.aimDirection;
            Vector3 aimingRight = Vector3.Cross(Vector3.up, aimingForward).normalized;

            Vector3 magnitude = aiEntity.data.moveDirection * aiEntity.data.moveSpeed / aiEntity.propertyDataConfig.move_speed;
            float animInputX = Vector3.Dot(magnitude, aimingRight);
            float animInputY = Vector3.Dot(magnitude, aimingForward);
            
            // Debug.LogError($"SetAnimatorProperties animInputX={animInputX}, animInputY={animInputY}, magnitude={magnitude}, aimingRight={aimingRight}, aimingForward={aimingForward}");
            
            _Animator.SetFloat(_animHorId,animInputX);
            _Animator.SetFloat(_animVerId, animInputY);

            if (_setSpeedAnimParam)
            {
                if (animInputY != 0)
                {
                    soundTime += Time.deltaTime;
                    if (soundTime > 0.3f)
                    {
                        AudioManager.PlaySound("am_street_npc_run_step",aiEntity.viewer.transform.gameObject);
                        soundTime = 0;
                    }
                }
                _Animator.SetFloat(_animSpeedId, Mathf.Clamp(npcData.moveSpeed/aiEntity.propertyDataConfig.move_speed, 0, npcData.maxSpeed*npcData.moveSpeedFactor));
            }
        }

        private void Move(Vector3 vector3)
        {
            Vector3 pos  = new Vector3(SyncTransform.position.x + vector3.x, SyncTransform.position.y, SyncTransform.position.z + vector3.z);
            seeker.aiPath.FinalizeMovement(pos,SyncTransform.rotation);  // 将AI限制在NavMesh范围内
            pos = seeker.aiPath.position;
            aiEntity.SetPositionAndRotation(pos);
            // AssignFixedLocation(pos);
            // AssignFixedRotation(SyncTransform.rotation.eulerAngles);
            // transform.localPosition = Vector3.zero;
            // transform.localEulerAngles = Vector3.zero;
        }
        private Quaternion mYawQuater = Quaternion.identity;
        // private void Rotate(Quaternion quaternion)
        private void UpdateRotation()
        {
            Quaternion finalRotate;
            do
            {
                if (immediateRotate)
                {
                    // 这边立即旋转模式 与 平滑旋转 算法不一样，两者不能及时切换。
                    Vector3 wpv = seeker.WaypointVector;
                    wpv.y = 0;
                    wpv.Normalize();
                    if (Mathf.Abs(wpv.x) > 0.001f || Mathf.Abs(wpv.z) > 0.001f)// Seeker 在水平方向没有速度时，不应转向
                    {
                        if (seeker.seekFrame <= banRotationFrame)   
                        {
                            // 禁止旋转
                            finalRotate = aiEntity.battleAgent.transform.rotation;
                        }
                        else
                        {
                            // 改为及时转向模式
                            finalRotate = Quaternion.LookRotation(wpv);
                        }
                        break;
                    }
                }
                
                // 改为及时转向模式
                finalRotate = mRootMotionRotation * seeker.transform.rotation;
                
            } while (false);
          
            Vector3 targetEulerAngles = finalRotate.eulerAngles;
            
            Vector3 targetDirection = (Quaternion.Euler(targetEulerAngles) * Vector3.forward).normalized;
            if (aiEntity.data.aimingState == AimingState.None)
            {
                // 非瞄准状态下，瞄准方向和移动方向一致
                aiEntity.data.aimDirection = targetDirection;
            }
            else
            {
                // 瞄准状态下，瞄准方向和移动方向不一致，角色旋转按照瞄准方向为准
                targetEulerAngles = Quaternion.LookRotation(aiEntity.data.aimDirection).eulerAngles;
            }
            mYawQuater = Quaternion.Euler(targetEulerAngles);
            // 插值旋转
            AssignFixedRotation(targetEulerAngles);
            // 记录移动方向
            if (seeker.WaypointDistance == 0)
            {
                aiEntity.data.moveDirection = seeker.transform.forward;
            }
            else
            {
                aiEntity.data.moveDirection = seeker.WaypointVector.normalized;
            }
        }

        private void UpdateMovement(float rDeltaTime)
        {
            // Override root motion if we're meant to

            // Get rid of root-motion that is not aligned with our input
            // float lMovementSpeed = rMovement.magnitude;
            float maxMoveSpeed = 5;
            AIEntity aiEntity = LibertyAIUtils.GetNpc(npcData.uniqueId);
            if (aiEntity != null)
            {
                maxMoveSpeed = aiEntity.propertyDataConfig.move_speed;
            }
            npcData.moveSpeed = Mathf.MoveTowards(npcData.moveSpeed, maxMoveSpeed * npcData.maxSpeed * npcData.moveSpeedFactor, AIData.moveAccelerrate*rDeltaTime);
        }
        
        void OnAnimatorMove()
        {
            if (_Animator.updateMode == AnimatorUpdateMode.Normal && Time.deltaTime == 0f)
            {
                mRootMotionMovement = Vector3.zero;
                mRootMotionRotation = Quaternion.identity;

                return;
            }

            //Log.FileWrite("OnAnimatorMove");

            // Clear any root motion values
            if (_Animator == null)
            {
                mRootMotionMovement = Vector3.zero;
                mRootMotionRotation = Quaternion.identity;
            }
            // Store the root motion as relative to the forward direction.
            else
            {
                // Convert the movement to relative the current rotation
                mRootMotionMovement = Quaternion.Inverse(SyncTransform.rotation) * (_Animator.deltaPosition);

                // We don't want delta time spikes to cause our character to move erratically. So, instead we
                // translate the movement using our smoothed delta time.

                // Store the rotation as a velocity per second.
                mRootMotionRotation = _Animator.deltaRotation;
            }
        }
       
        private void OnDestroy()
        {
            seeker = null;
            npcData = null;
        }
    }
}
